iT邦幫忙

2024 iThome 鐵人賽

DAY 26
0

MVP架構是一個很重要的東西
它的存在幫助可以增加程式的可讀性也能方便之後的維修,是個很方便的架構

介紹MVP

V(View)
負責顯示UI和處理使用者互動
如顯示數據或處理點擊事件之類的
但View本身並不處理邏輯,這些邏輯會在交給Presenter去處理
P(Presenter)
放邏輯判斷的地方
像是在達成什麼條件時會執行什麼動作,那個判斷有沒有達成條件就是Presenter
它在接收到View的用戶操作指令後,會從Model裡獲取數據並將結果返回給View執行下一個步驟
M(Model)
是一個類似資料庫的東西
當需要拿取它負責管理的數據時,可以透過Model拿取
像是拿取登入者基本訊息之類的或帳密之類的
除了這三個以外還有一個特殊的東西叫做Contract
Contract
是用來讓View、Presenter、Modle互相溝通的一個Interface
將我們會在當前Activity用到的方法在這先新增
等等在其他Activity要用到這些方法時才不會錯誤

範例

這邊我設計了簡單的登入程式
首先在View先登入並將輸入的資料傳送資料給Presenter
然後在Presenter做帳密判斷前先到Model裡拿取唯一正確的帳密
如帳密錯誤就叫View顯示Toast
若是正確就叫View顯示Toast並跳轉到登入成功的畫面
(Model裡帳密設定成test和1234)

在剛開始時,我們會開啟三個Activity再加上一個Interface
https://ithelp.ithome.com.tw/upload/images/20241004/2016845614oQ4nVwAc.png
所以總共會有四個Activity
但因為我還有用了一個跳轉頁面的功能,所以這邊多開了一個Activty和xml
https://ithelp.ithome.com.tw/upload/images/20241004/201684563VHJccoQCg.png
https://ithelp.ithome.com.tw/upload/images/20241004/20168456iZQaR0Jifk.png
不過那個頁面就只有xml有放ImageView和TextView而已,Activity並沒有動作所以這邊就不另外放上來和解釋程式碼了

  • 執行程式時
    https://ithelp.ithome.com.tw/upload/images/20241004/20168456hrm07riWFG.png
  • 輸入錯誤的密碼顯示的Toast
    https://ithelp.ithome.com.tw/upload/images/20241004/20168456lFUj3XAag5.png
  • 輸入正確的帳密會顯示Toast並跳轉到登入成功的頁面
    https://ithelp.ithome.com.tw/upload/images/20241004/20168456bPMJAwKbKl.png
    https://ithelp.ithome.com.tw/upload/images/20241004/20168456X73NG83IOD.png

下面是程式碼和程式註解

MainActivity(View)

public class MainActivity extends AppCompatActivity implements MainContract.view {
    private EditText accountEt, passwordEt;
    private Button loginBtn;
    private MainPresenter presenter;
    //設定變數
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        accountEt = findViewById(R.id.main_account_et);
        passwordEt = findViewById(R.id.main_password_et);
        loginBtn = findViewById(R.id.main_login_btn);
        //綁定變數並初始化
        presenter = new MainPresenter(this);
        //New出Presenter,並把自己丟入。

        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String account = accountEt.getText().toString();
                String password = passwordEt.getText().toString();
                //將accountEt和passwordEt裡的內容轉成字串並丟給設定的變數

                presenter.Login(account, password);
                //再將兩個變數丟給presenter去處理
            }
        });
    }
    @Override
    public void LoginSuccess() {
        Toast.makeText(this, "登入成功", Toast.LENGTH_SHORT).show();
        //Toast顯示登入成功
        Intent intent = new Intent(this, LoginSuccess.class);
        startActivity(intent);
        //跳轉到LoginSuccess
    }
    @Override
    public void LoginError() {
        Toast.makeText(this, "登入失敗,請檢查帳號或密碼", Toast.LENGTH_SHORT).show();
        //Toast顯示登入失敗,請檢查帳號或密碼
    }
}

跟使用者看到的畫面有關
如元件的綁定、元件的點擊事件、登入成功或失敗使用者能看到的動作

MainPresenter(Presenter)

public class MainPresenter implements MainContract.presenter {
    private MainContract.view view;
    private MainModel model;
    public MainPresenter(MainContract.view view) {
        this.view = view;
        //接收view
        model = new MainModel(this);
        //初始化Modle並把自己丟入
    }

    public void Login(String username, String password) {
        boolean isSuccess = model.LoginData(username, password);
        //判斷傳送過來的帳密與MainModel裡設定的帳密是否相同
        if (isSuccess) {
            view.LoginSuccess();
            //通知view顯示登入成功並執行登入成功後的操作
        } else {
            view.LoginError();
            //通知view顯示登入失敗並執行登入失敗後的操作
        }
    }
}

判斷輸入的帳密與Model裡設定的帳密一樣不一樣
一樣就執行View裡的LoginSuccess
不一樣就執行View裡的LoginError

MainModel(Model)

public class MainModel implements MainContract.model {
    private MainContract.presenter presenter;
    public MainModel(MainContract.presenter presenter) {
        this.presenter = presenter;
        //接收presenter
    }

    public boolean LoginData(String username, String password) {
        return username.equals("test") && password.equals("1234");
        //回傳正確的帳號是test且正確的密碼為1234
    }
}

將預設的帳號丟給Presenter去做判斷
回傳正確的帳號和密碼為test和1234

MainContract(Contract)

public interface MainContract {
    interface view{
        void LoginSuccess();
        void LoginError();
    }
    interface presenter{
        void Login(String username, String password);
    }
    interface model{
        boolean LoginData(String username, String password);
    }
}

連接三個Activity的橋樑
將在其他Activity以外會使用到的方法都寫在這裡即可正常使用
這樣就可以簡單的完成一個登入的MVP了

下面附上兩個Activity完整的xml

activity_main

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:layout_weight="1">

        <TextView
            android:id="@+id/main_title_tv"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="0.1"
            android:gravity="center"
            android:text="Login"
            android:textSize="25dp"
            android:textStyle="bold" />

        <EditText
            android:id="@+id/main_account_et"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="0.1"
            android:hint="帳號"
            android:inputType="text"/>

        <EditText
            android:id="@+id/main_password_et"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:hint="密碼"
            android:layout_weight="0.1"
            android:inputType="textPassword" />

        <Button
            android:id="@+id/main_login_btn"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="0.1"
            android:text="登入" />

        <View
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />
    </LinearLayout>
</LinearLayout>

activity_login_success

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".LoginSuccess">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:layout_editor_absoluteX="1dp"
        tools:layout_editor_absoluteY="1dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:orientation="horizontal">

            <View
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1" />

            <ImageView
                android:id="@+id/imageView"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                app:srcCompat="@drawable/checked" />

            <View
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1" />
        </LinearLayout>

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="0.2"
            android:gravity="center"
            android:textSize="50dp"
            android:textStyle="bold"
            android:text="登入成功" />

        <View
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />

    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

下篇會介紹SharedPrefences


上一篇
[Day 25] TabLayout+ViewPager2介紹(下)
下一篇
[Day 27] SharedPreferences介紹
系列文
深入Android Java程式語言 - 打造我的行動應用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言